#ifndef __json_kenny_ijeiji8e3__
#define __json_kenny_ijeiji8e3__

#include <cstdint>
#include <type_traits>
#include <vector>
#include <list>
#include <string>

#include "../import/json.hpp"
#include "../base/optional.hpp"

namespace hude
{
    namespace utils
    {
        using json_t = ::nlohmann::json;

        namespace _details
        {
            /*********************************************************************
            * @brief ...
            */
            template< typename _type > struct _is_type_t;

            template<> struct _is_type_t< std::decay< json_t   >::type                > { inline static bool is( const json_t& node ) { return node.is_object(); } };
            template<> struct _is_type_t< std::decay< bool     >::type                > { inline static bool is( const json_t& node ) { return node.is_boolean(); } };
            template<> struct _is_type_t< std::decay< int8_t   >::type                > { inline static bool is( const json_t& node ) { return node.is_number_integer(); } };
            template<> struct _is_type_t< std::decay< int16_t  >::type                > { inline static bool is( const json_t& node ) { return node.is_number_integer(); } };
            template<> struct _is_type_t< std::decay< int32_t  >::type                > { inline static bool is( const json_t& node ) { return node.is_number_integer(); } };
            template<> struct _is_type_t< std::decay< int64_t  >::type                > { inline static bool is( const json_t& node ) { return node.is_number_integer(); } };
            template<> struct _is_type_t< std::decay< uint8_t  >::type                > { inline static bool is( const json_t& node ) { return node.is_number_unsigned(); } };
            template<> struct _is_type_t< std::decay< uint16_t >::type                > { inline static bool is( const json_t& node ) { return node.is_number_unsigned(); } };
            template<> struct _is_type_t< std::decay< uint32_t >::type                > { inline static bool is( const json_t& node ) { return node.is_number_unsigned(); } };
            template<> struct _is_type_t< std::decay< uint64_t >::type                > { inline static bool is( const json_t& node ) { return node.is_number_unsigned(); } };
            template<> struct _is_type_t< std::decay< float    >::type                > { inline static bool is( const json_t& node ) { return node.is_number_float(); } };
            template<> struct _is_type_t< std::decay< double   >::type                > { inline static bool is( const json_t& node ) { return node.is_number_float(); } };
            template< typename _type > struct _is_type_t< std::vector< _type >        > { inline static bool is( const json_t& node ) { return node.is_array(); } };
            template< typename _type > struct _is_type_t< std::list< _type >          > { inline static bool is( const json_t& node ) { return node.is_array(); } };
            template< typename _char > struct _is_type_t< std::basic_string< _char >  > { inline static bool is( const json_t& node ) { return node.is_string(); } };

            /*********************************************************************
            * @brief ...
            */
            template< typename _ret >
            struct _json_value_t
            {
                using ret_t = typename std::remove_cv< typename std::remove_reference< _ret >::type >::type;
                using result_t = hude::utils::optional_t< _ret >;

                inline static bool _ok( const json_t& json, const char* key )
                {
                    return ( json.is_object() && json.find( key ) != json.end() && _is_type_t< ret_t >::is( json[key] ) );
                }

                inline static result_t _value( const json_t& json, const char* key )
                {
                    if( _ok( json, key ) )
                    {
                        return result_t{ json.get< ret_t >() };
                    } else {
                        return result_t{};
                    }
                }

                inline static result_t _value( const json_t& json, const std::string& key )
                {
                    return _value( json, key.c_str() );
                }

                hude::optional_t< json_t > target_;

                _json_value_t( const json_t& json, const char* key ) :
                    target_( ( _ok( json, key ) )? json[key] : hude::optional_t< json_t >{ hude::optional_null_t{} } ) {}

                inline bool ok() const { return !!target_; }
                inline _ret value() const { return target_->get< _ret >(); }

                inline json_t& ref() { return *target_; }
                inline const json_t& ref() const { return *target_; }

                inline operator bool() const { return ok(); }
                inline _ret operator*() const { return value(); }

                inline json_t* operator->() { return &(*target_); }
                inline const json_t* operator->() const { return &(*target_); }
            };

            /*********************************************************************
            * @brief 从指定的json路径获取值
            */
            struct _json_path_t
            {
                template< typename ... _str >
                inline static hude::optional_t< json_t::const_iterator > path_from( const json_t& json, _str&& ... args );
                template< typename ... _str >
                inline static hude::optional_t< json_t::iterator > path_from( json_t& json, _str&& ... args );

                template< typename _value, typename ... _str >
                inline static _value get_or_default_from( const json_t& json, const _value& def, _str&& ... args );

                inline static hude::optional_t< json_t::const_iterator > _path( const json_t& json, std::initializer_list< std::string >&& keys );
                inline static hude::optional_t< json_t::iterator > _path( json_t& json, std::initializer_list< std::string >&& keys );
            };

            /*********************************************************************
            * @brief 取指定json路径的json_t迭代器
            */
            template< typename ... _str >
            inline hude::optional_t< json_t::const_iterator > _json_path_t::path_from( const json_t& json, _str&& ... args )
            {
                return _path( json, { std::forward< _str >( args ) ... } );
            }

            /*********************************************************************
            * @brief 取指定json路径的json_t迭代器
            */
            template< typename ... _str >
            inline hude::optional_t< json_t::iterator > _json_path_t::path_from( json_t& json, _str&& ... args )
            {
                return _path( json, { std::forward< _str >( args ) ... } );
            }

            /*********************************************************************
            * @brief 取指定json路径的值，如果不存在则指定默认值
            */
            template< typename _value, typename ... _str >
            inline _value _json_path_t::get_or_default_from( const json_t& json, const _value& def, _str&& ... args )
            {
                auto itr = _path( json, { std::forward< _str >( args ) ... } );
                return itr? (*itr)->template get< _value >() : def;
            }

            /*********************************************************************
            * @brief 取指定json路径的json_t迭代器
            */
            inline hude::optional_t< json_t::const_iterator > _json_path_t::_path( const json_t& json, std::initializer_list< std::string >&& keys )
            {
                if( keys.size() == 0U ) return hude::optional_null_t();

                auto k = keys.begin();
                auto j = json.find( *k );

                if( j == json.cend() ) return hude::optional_null_t();

                for( k += 1; k != keys.end(); ++k )
                {
                    auto t = j->find( *k );
                    if( t == j->cend() ) return hude::optional_null_t();
                    j = t;
                }

                return j;
            }

            /*********************************************************************
            * @brief 取指定json路径的json_t迭代器
            */
            inline hude::optional_t< json_t::iterator > _json_path_t::_path( json_t& json, std::initializer_list< std::string >&& keys )
            {
                if( keys.size() == 0U ) return hude::optional_null_t();

                auto k = keys.begin();
                auto j = json.find( *k );

                if( j == json.end() ) return hude::optional_null_t();

                for( k += 1; k != keys.end(); ++k )
                {
                    auto t = j->find( *k );
                    if( t == j->end() ) return hude::optional_null_t();
                    j = t;
                }

                return j;
            }

            /*********************************************************************
            * @brief json格式检测
            */
            template< typename _itr >
            struct json_checker_t
            {
                using itr_t = _itr;

                inline bool operator()( itr_t& h, itr_t& b, itr_t e, bool root = true ) const
                {
                    return _json( h, b, e, root );
                }

                inline static bool _json( itr_t& h, itr_t& b, itr_t e, bool root )
                {
                    itr_t c = b;
                    itr_t s = e;
                    bool r = _space( s, c, e );

                    if( c >= e ) return false;

                    h = c;

                    if( root )
                    {
                        switch ( *c )
                        {
                        case '{' :    r = _object( s, c, e );     break;
                        case '[' :    r = _array( s, c, e );      break;
                        case '/' :    r = _comment( s, c, e );    break;
                        default  :    r = false;                         break;
                        }
                    } else {
                        switch ( *c )
                        {
                        case '{' :    r = _object( s, c, e );     break;
                        case '[' :    r = _array( s, c, e );      break;
                        case 't' :    r = _true( s, c, e );       break;
                        case 'f' :    r = _false( s, c, e );      break;
                        case '"' :    r = _string( s, c, e );     break;
                        case '/' :    r = _comment( s, c, e );    break;
                        default  :    r = _number( s, c, e );     break;
                        }
                    }

                    b = ( c < e )? c : e;

                    return r? true : ( h = e, false );
                }

                inline static bool _space( itr_t& h, itr_t& b, itr_t e )
                {
                    auto is_space = []( char v ) { return v == ' ' || v == '\t' || v == '\r' || v == '\n'; };

                    h = b;

                    while( b < e && is_space( *b ) ) ++b;

                    h = ( h == b )? e : h;

                    return h < b;
                }

                inline static bool _cmp_str( itr_t& h, itr_t& b, itr_t e, const char* target, size_t len )
                {
                    if( e - b < len ) return h = e, false;

                    auto ptr = (const char*)( &(*b) );

                    auto r = strncmp( target, ptr, 4U );

                    return ( r == 0 )?
                        ( h = b,    b += len,    true  ) :
                        ( h = e,                 false );
                }

                inline static bool _true( itr_t& h, itr_t& b, itr_t e ) { return _cmp_str( h, b, e, "true", 4U ); }
                inline static bool _false( itr_t& h, itr_t& b, itr_t e ) { return _cmp_str( h, b, e, "false", 5U ); }

                inline static bool _number( itr_t& h, itr_t& b, itr_t e )
                {
                    auto is_num = []( char v ) { return ( v >= '0' && v <= '9' ) || v == '.'; };

                    if( *b == '-' || is_num( *b ) )
                    {
                        h = b++;

                        while( b < e && is_num( *b ) ) ++b;
                    } else {
                        h = e;
                    }

                    return h < b;
                }

                inline static bool _object( itr_t& h, itr_t& b, itr_t e )
                {
                    itr_t c = b + 1;
                    itr_t s = e;
                    bool r = false;

                    if( *b == '{' )
                    {
                        while( c < e )
                        {
                            _space( s, c, e );
                            if( c >= e ) break;

                            if( *c == '}' ) { ++c; r = true; break; }

                            if( *c != '"' || ! _string( s, c, e ) ) break;

                            _space( s, c, e );
                            if( c >= e || *c != ':' ) break;

                            _space( s, ++c, e );
                            if( c >= e || ! _json( s, c, e, false ) ) break;

                            _space( s, c, e );
                            if( c >= e ) break;

                            if( *c == ',' ) ++c;

                            r = false;
                        }
                    }

                    h = r? b : e;

                    b = c;

                    return r;
                }

                inline static bool _array( itr_t& h, itr_t& b, itr_t e )
                {
                    itr_t c = b + 1;
                    itr_t s = e;
                    bool r = false;

                    if( *b == '[' )
                    {
                        while( c < e )
                        {
                            _space( s, c, e );
                            if( c >= e ) break;

                            if( *c == ']' )
                            {
                                r = true;
                                c += 1;
                                break;
                            }

                            r = _json( s, c, e, false );
                            if( !r ) break;

                            _space( s, c, e );
                            if( c >= e ) break;

                            if( *c == ',' ) ++c;

                            r = false;
                        }
                    }

                    h = r? b : e;
                    b = c;

                    return r;
                }

                inline static bool _string( itr_t& h, itr_t& b, itr_t e )
                {
                    bool transfer = false;

                    if( *b == '"' )
                    {
                        for( b = b + 1; b < e; ++b )
                        {
                            if( transfer )
                            {
                                transfer = false;
                                continue;
                            }

                            if( *b == '"' )
                            {
                                h = b;
                                b = b + 1;
                                return true;
                            }
                            
                            if( *b == '\\' ) {
                                transfer = true;
                            }
                        }
                    }

                    h = e;

                    return false;
                }

                inline static bool _comment( itr_t& h, itr_t& b, itr_t e )
                {
                    return false;
                }
            };
        }

        template< typename _ret >
        using json_get_t = _details::_json_value_t< _ret >;

        using json_path_t = _details::_json_path_t;

        template< typename _ret, typename _key >
        inline hude::optional_t< _ret > json_value( const json_t& json, _key&& key )
        {
            return json_get_t< _ret >::_value( json, std::forward< _key >( key ) );
        }

    }
}

#endif  // __json_kenny_ijeiji8e3__
